import { Child, Command, open } from '@tauri-apps/api/shell';
import { platform } from '@tauri-apps/api/os';
import { logger } from './logger';
// 命令子程序的集合
const cmdMap = new Map<string, Child>();
// 项目和项目下的子程序集合
const codeCmdMap = new Map<string, string[]>();

// windows环境下不需要在命令中添加.cmd
const noNeedCmdMap: Map<string, number> = ['git'].reduce((p, c) => {
  p.set(c, 1);
  return p;
}, new Map());

const supportCmdMap: Map<string, number> = [
  'npm',
  'yarn',
  'node',
  'code',
  'kill',
  'nrm',
  'pwd',
  'ls',
  'open',
  'start',
  'git',
].reduce((p, c) => {
  p.set(c, 1);
  return p;
}, new Map());

export async function getCommandByPlatform(cmd: string) {
  const platformName = await platform();
  const cmdStr =
    /^win/i.test(platformName) && !noNeedCmdMap.has(cmd) ? `${cmd}.cmd` : cmd;
  return cmdStr;
}

// 运行指令
export function exceCommand(commandParams: {
  terminalkey: string;
  workSpace: string;
  command: string;
  code: string;
}) {
  return new Promise(async (resolve, reject) => {
    const { terminalkey, workSpace, command, code } = commandParams;
    if (!command) {
      logger('', terminalkey);
      return resolve(0);
    }
    try {
      logger(`[READY] :  校验命令有效性`, terminalkey);
      const [cmdStr, ...args] = command.split(/\s+/);
      if (!supportCmdMap.has(cmdStr)) {
        logger(`[END]   :  ${command}不支持执行！`, terminalkey);
        return resolve(0);
      }
      logger(`[START] :  ${command}`, terminalkey);
      const cmd = new Command(await getCommandByPlatform(cmdStr), args, {
        cwd: workSpace,
      });
      cmd.on('close', (data) => {
        const msg = `[END]   :  ${command} finished with code ${data.code} and signal ${data.signal}`;
        console.log(msg);
        logger(msg, terminalkey);
      });
      cmd.on('error', (error) => {
        const msg = `[ERROR] :  ${error}`;
        console.error(msg);
        logger(msg, terminalkey);
      });
      cmd.stdout.on('data', (line) => {
        const msg = `[INFO]  :  ${line}`;
        console.log(msg);
        logger(msg, terminalkey);
      });
      cmd.stderr.on('data', (line) => {
        const msg = `[STDERR]:  ${line}`;
        console.log(msg);
        logger(msg, terminalkey);
      });

      const child = await cmd.spawn();
      const pid = (child || {}).pid;
      const msg = `[PID]   :  ${pid}`;
      console.log(msg);
      logger(msg, terminalkey);
      // 将pid和code挂钩
      addPidToCode(code, terminalkey);
      addChildToMap(terminalkey, child as Child);
      resolve(pid);
    } catch (e) {
      const msg = `[END]   :${e}`;
      logger(msg, terminalkey);
      reject(e);
    }
  });
}

// 打开vsCode
export async function openVsCodeRun(workSpace: string) {
  try {
    const cmd = new Command(await getCommandByPlatform('code'), ['./'], {
      cwd: workSpace,
    } as any);
    await cmd.spawn();
  } catch (e) {
    console.log('openVsCodeRun', e);
  }
}

export async function openDevWeb(devUrl: string) {
  try {
    open(devUrl);
  } catch (e) {
    console.log('openDevWeb', e);
  }
}

// 停止所有进程
export function stopAllProcess() {
  Object.entries(codeCmdMap).forEach(([code]) => stopProject(code));
}

// 停止对应项目的所有进程
export async function stopProject(code: string) {
  const tkeys = codeCmdMap.get(code);
  if (Array.isArray(tkeys)) {
    tkeys.forEach(stopProcess);
  }
}
// 停止进程
export async function stopProcess(terminalkey: string) {
  try {
    if (cmdMap.has(terminalkey)) {
      const child = cmdMap.get(terminalkey);
      if (child?.pid) {
        await closeChild(child.pid);
      }
      cmdMap.delete(terminalkey);
    }
  } catch (e) {
    console.log('stopProcess', e);
  }
}
// 将pid和code挂钩
function addPidToCode(code: string, terminalkey: string) {
  if (!codeCmdMap.has(code)) {
    codeCmdMap.set(code, []);
  }
  const pidList = codeCmdMap.get(code) as string[];
  pidList?.push(terminalkey);
  codeCmdMap.set(code, pidList);
}
// 将pid和child挂钩
async function addChildToMap(terminalkey: string, cmd: Child) {
  if (cmdMap.has(terminalkey)) {
    const child = cmdMap.get(terminalkey);
    cmdMap.delete(terminalkey);
    if (child) {
      await closeChild(child.pid);
    }
  }
  cmdMap.set(terminalkey, cmd);
}

async function closeChild(pid: number) {
  if (!/^\d+$/.test(`${pid}`)) return;
  const platformName = await platform();
  const isWin = /^win/i.test(platformName);
  if (isWin) {
    await new Command('taskkill', ['/pid', `${pid}`, '-f']).spawn();
  } else {
    await new Command('kill', [`${pid}`]).spawn();
  }
}
